//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.eResearch.Publisher
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using Microsoft.Research.eResearch.Blackboard.Common;
    using Microsoft.Research.eResearch.Common.Eventing;
    using Microsoft.Research.eResearch.Common.Logging;
    using Microsoft.Research.eResearch.Common.Reflection;
    using Microsoft.Research.eResearch.Publisher.Interfaces;

    /// <summary>
    /// The BlackboardPublisher class can publish new messages to the Blackboard and listen 
    /// for updates concerning its subscription profile.
    /// </summary>
    public class BlackboardPublisher : ILoggable
    {
        /// <summary>
        /// Address of the subscription receiver host.
        /// </summary>
        private Uri returnAddress;

        /// <summary>
        /// Instance of the message sender.
        /// </summary>
        private IPublisherMessageSender publisherMessageSender;

        /// <summary>
        /// Instance of the subscription receiver.
        /// </summary>
        private IPublisherSubscriptionReceiver publisherSubscriptionReceiver;

        /// <summary>
        /// Last subscription profile that was received.
        /// </summary>
        private SubscriptionProfile lastSubscriptionProfile = new SubscriptionProfile();

        /// <summary>
        /// Initializes a new instance of the <see cref="BlackboardPublisher"/> class.
        /// </summary>
        public BlackboardPublisher()
        {
            publisherMessageSender = ReflectionHelper.CreateInstance<IPublisherMessageSender>("IPublisherMessageSender");
            publisherSubscriptionReceiver = ReflectionHelper.CreateInstance<IPublisherSubscriptionReceiver>("IPublisherSubscriptionReceiver");
            publisherSubscriptionReceiver.PublisherSubscriptionChanged += OnPublisherSubscriptionChanged;
        }

        /// <summary>
        /// Raised when a new subscription profile has been received.
        /// </summary>
        public event EventHandler<PublisherSubscriptionChangedEventArgs> PublisherSubscriptionChanged;

        /// <summary>
        /// Fired when a log message is received.
        /// </summary>
        public event EventHandler<LogEventArgs> LogMessageReceived;

        /// <summary>
        /// Gets the last subscription profile that was received.
        /// </summary>
        /// <value>The last subscription profile.</value>
        public SubscriptionProfile LastSubscriptionProfile
        {
            get { return lastSubscriptionProfile; }
            private set { lastSubscriptionProfile = new SubscriptionProfile(value); }
        }

        /// <summary>
        /// Starts the BlackboardPublisher.  This involves contacting the Blackboard to obtain its initial subscription profile
        /// as well as starting a service to listen for profile updates from the Blackboard.
        /// </summary>
        public void Start()
        {
            if (publisherSubscriptionReceiver != null)
            {
                Log("Attempting to start...");
                returnAddress = publisherSubscriptionReceiver.Start();
            }
            
            this.LastSubscriptionProfile = publisherMessageSender.Register(returnAddress);
        } 

        /// <summary>
        /// Stops the BlackboardPublisher.  This involves notifying the Blackboard that is is unregistering
        /// as well as stopping the service that listens for profile updates.
        /// </summary>
        public void Stop()
        {
            if (publisherMessageSender != null)
            {
                Log("Attempting to unregister...");
                publisherMessageSender.Unregister(returnAddress);
            }

            if (publisherSubscriptionReceiver != null)
            {
                Log("Attempting to stop...");
                publisherSubscriptionReceiver.Stop();
            }
        } 

        /// <summary>
        /// Publishes the specified message to the Blackboard.
        /// </summary>
        /// <param name="message">The message.</param>
        public void Publish(BlackboardMessage message)
        {
            if (publisherMessageSender != null)
            {
                // If message having restricted concept, don't publish the message.
                foreach (KeyValuePair<string, string> item in message)
                {
                    if (BlackBoardSettings.BlockedConcepts.IsConceptBlocked(item.Key))
                    {
                        // If required, we can log the blocked messages here.
                        return;
                    }
                }

                Log("Attempting to publish...");
                publisherMessageSender.Publish(message);
            }
        }

        /// <summary>
        /// Raises the PublisherSubscriptionChanged event.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="Microsoft.Research.eResearch.Publisher.Interfaces.PublisherSubscriptionChangedEventArgs"/> instance containing the event data.</param>
        private void OnPublisherSubscriptionChanged(object sender, PublisherSubscriptionChangedEventArgs e)
        {
            this.LastSubscriptionProfile = e.IncomingSubscriptionProfile;
            this.PublisherSubscriptionChanged.Fire(this, e);
        } 

        /// <summary>
        /// Raises the <see cref="E:LogMessageReceived"/> event.
        /// </summary>
        /// <param name="args">The <see cref="Microsoft.Research.eResearch.Common.Logging.LogEventArgs"/> instance containing the event data.</param>
        private void OnLogMessageReceived(LogEventArgs args)
        {
            this.LogMessageReceived.Fire(this, args);
        }

        /// <summary>
        /// Log the message.
        /// </summary>
        /// <param name="message">Message text to be logged.</param>
        private void Log(string message)
        {
            OnLogMessageReceived(new LogEventArgs("Debug", String.Format(CultureInfo.CurrentUICulture, "BlackboardPublisher: {0}", message)));
        }
    }
}
